home *** CD-ROM | disk | FTP | other *** search
- Subject: v22i046: NN Newsreader, release 6.4, Part11/21
- Newsgroups: comp.sources.unix
- Approved: rsalz@uunet.UU.NET
- X-Checksum-Snefru: bdd7a059 3aee2592 7e0d893e 7da71f7b
-
- Submitted-by: "Kim F. Storm" <storm@texas.dk>
- Posting-number: Volume 22, Issue 46
- Archive-name: nn6.4/part11
-
- #! /bin/sh
- # This is a shell archive. Remove anything before this line, then feed it
- # into a shell via "sh file" or similar. To overwrite existing files,
- # type "sh file -c".
- # The tool that generated this appeared in the comp.sources.unix newsgroup;
- # send mail to comp-sources-unix@uunet.uu.net if you want that tool.
- # Contents: init.c kill.c sort.c
- # Wrapped by storm@texas.dk on Sun May 6 18:19:45 1990
- PATH=/bin:/usr/bin:/usr/ucb ; export PATH
- echo If this archive is complete, you will see the following message:
- echo ' "shar: End of archive 11 (of 22)."'
- if test -f 'init.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'init.c'\"
- else
- echo shar: Extracting \"'init.c'\" \(21451 characters\)
- sed "s/^X//" >'init.c' <<'END_OF_FILE'
- X/*
- X * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
- X *
- X * .nn/init file handling
- X */
- X
- X
- X#include "config.h"
- X#include "articles.h"
- X#include "term.h"
- X#include "keymap.h"
- X#include "menu.h"
- X
- Ximport char *help_directory, *db_directory;
- X
- Xexport int in_init = 0; /* true when parsing init file */
- Xexport int alt_cmd_key; /* K_ when parse_command returns AC_KEYCMD */
- X
- Xstatic int init_err = 0; /* errors in init file */
- X
- X
- X/*VARARGS*/
- Xinit_message(va_alist)
- Xva_dcl
- X{
- X char *fmt;
- X use_vararg;
- X
- X start_vararg;
- X
- X if (in_init) {
- X fmt = va_arg1(char *);
- X
- X printf("init error: ");
- X vprintf(fmt, va_args2toN);
- X putchar(NL);
- X init_err++;
- X } else
- X vmsg(va_args1toN);
- X
- X end_vararg;
- X}
- X
- X
- X#define MAXARG 10
- X
- Xstatic char *argvec[MAXARG + 2];
- Xstatic int argc;
- X
- Xstatic char *strip_str(cmd)
- Xregister char *cmd;
- X{
- X if (cmd == NULL) return cmd;
- X
- X while (*cmd && isspace(*cmd)) cmd++;
- X if (*cmd == NUL || *cmd == NL) return NULL;
- X
- X return cmd;
- X}
- X
- X
- Xstatic split_command(cmd)
- Xregister char *cmd;
- X{
- X /* split command string */
- X
- X for (argc = 0; argc < MAXARG + 2; argc++) argvec[argc] = NULL;
- Xstrip_more:
- X if ((cmd = strip_str(cmd)) == NULL || *cmd == '#') return 0;
- X if (*cmd == ':') {
- X cmd++;
- X goto strip_more;
- X }
- X
- X argc = 0;
- X argvec[0] = cmd;
- X
- X if (in_init)
- X while (*cmd) {
- X if (*cmd == NL) {
- X *cmd = NUL;
- X break;
- X }
- X cmd++;
- X }
- X
- X return 1;
- X}
- X
- Xstatic char *argv(i)
- Xint i;
- X{
- X register char *cmd;
- X
- X if (i > MAXARG) return NULL;
- X
- X if (argc <= i)
- X if (cmd = argvec[argc])
- X while (argc <= i) {
- X while (*cmd && !isspace(*cmd)) cmd++;
- X if (*cmd == NUL) {
- X argc = MAXARG;
- X break;
- X }
- X
- X *cmd++ = NUL;
- X if ((cmd = strip_str(cmd)) == NULL) {
- X argc = MAXARG;
- X break;
- X }
- X argvec[++argc] = cmd;
- X }
- X else
- X argc = MAXARG;
- X
- X return argvec[i];
- X}
- X
- Xstatic is_sequence(cmd)
- Xchar *cmd;
- X{
- X if (!split_command(cmd)) return 0;
- X if ((cmd = argv(0)) == NULL) return 0;
- X return strcmp(cmd, "sequence") == 0;
- X}
- X
- X#define START_SEQUENCE 555
- X
- Xstatic load_init_file(name, seq_hook_ptr, only_seq)
- Xchar *name;
- XFILE **seq_hook_ptr;
- X{
- X FILE *init;
- X char cmdbuf[512], *cmd, *term;
- X extern char *term_name;
- X
- X /* use cmdbuf temporarily (to handle @ expansion) */
- X for (cmd = cmdbuf; *name; name++)
- X if (*name == '@') {
- X term = term_name;
- X while (term && *term) *cmd++ = *term++;
- X } else
- X *cmd++ = *name;
- X *cmd = NUL;
- X name = cmdbuf;
- X
- X if (strchr(name, '/') == NULL)
- X name = relative(nn_directory, name);
- X
- X init = open_file(name, OPEN_READ);
- X if (init == NULL) return;
- X
- X while (fgets(cmdbuf, 512, init)) {
- X if (only_seq) {
- X if (!is_sequence(cmdbuf)) continue;
- X *seq_hook_ptr = init;
- X return;
- X }
- X /* we use AC_REDRAW to avoid !-commands clear the screen */
- X if (parse_command(cmdbuf, AC_REDRAW, init) == START_SEQUENCE) {
- X if (seq_hook_ptr) {
- X *seq_hook_ptr = init;
- X return; /* no close !! */
- X } else {
- X init_message("load file contains 'sequence'");
- X fclose(init);
- X return;
- X }
- X }
- X }
- X
- X fclose(init);
- X}
- X
- Xvisit_init_file(only_seq, first_arg)
- Xint only_seq;
- Xchar *first_arg;
- X{
- X extern FILE *loc_seq_hook, *glob_seq_hook;
- X char *next_arg;
- X
- X if (first_arg && strncmp(first_arg, "-I", 2) == 0) {
- X if (first_arg[2] == NUL) return;
- X first_arg += 2;
- X } else
- X first_arg = ",init";
- X
- X in_init = 1;
- X while (first_arg) {
- X next_arg = strchr(first_arg, ',');
- X if (next_arg) *next_arg++ = NUL;
- X
- X if (*first_arg == NUL) {
- X if (glob_seq_hook == NULL)
- X load_init_file(relative(lib_directory, "init"), &glob_seq_hook, only_seq);
- X } else {
- X if (loc_seq_hook != NULL) {
- X fclose(loc_seq_hook);
- X loc_seq_hook = NULL;
- X }
- X load_init_file(first_arg, &loc_seq_hook, only_seq);
- X }
- X first_arg = next_arg;
- X }
- X
- X if (init_err) nn_exit(1);
- X in_init = 0;
- X}
- X
- X
- X/*
- X * parse a command (also :-commands)
- X */
- X
- Xstatic char *sw_string;
- X
- X#define SWITCH(str) \
- X for (sw_string = str; sw_string; sw_string = NULL)
- X
- X#define CASE(str) \
- X if (strcmp(sw_string, str) == 0)
- X
- X
- X#define ARG(i, str) (argv(i) && strcmp(argv(i), str) == 0)
- X#define ARGVAL(i) atol(argv(i))
- X#define ARGTAIL argvec[argc]
- X
- Xstruct alt_commands {
- X char *alt_name;
- X int alt_len;
- X int alt_type;
- X} alt_commands[] = {
- X "admin", 5, 0,
- X "bug", 3, 0,
- X "cd", 2, 1,
- X "compile", 7, 0,
- X "coredump", 8, 0,
- X "cost", 4, 0,
- X "decode", 6, 0,
- X "define", 6, 0,
- X "help", 4, 2,
- X "local", 5, 3,
- X "man", 3, 0,
- X "map", 3, -1,
- X "map both", 8, 4,
- X "map key", 7, 0,
- X "map menu", 8, 4,
- X "map show", 8, 4,
- X "mkdir", 5, 1,
- X "patch", 5, 0, /* QUICK HACK */
- X "post", 4, 0, /* QUICK HACK */
- X "print", 5, 0, /* QUICK HACK */
- X "pwd", 3, 0,
- X "rmail", 5, 0,
- X "set", 3, 3,
- X "show", 4, -1,
- X "show groups", 11, -1,
- X "show groups all", 15, 0,
- X "show groups subscr", 18, 0,
- X "show groups total", 17, 0,
- X "show groups unsub", 17, 0,
- X "show kill", 9, 0,
- X "show map", 8, -1,
- X "show map #", 10, 0,
- X "show map key", 12, 0,
- X "show map menu", 13, 0,
- X "show map show", 13, 0,
- X "show rc ", 8, 0,
- X "sort", 4, -1,
- X "sort arrival", 12, 0,
- X "sort date", 9, 0,
- X "sort lexical", 12, 0,
- X "sort sender", 11, 0,
- X "sort subject", 12, 0,
- X "toggle", 6, 3,
- X "unread", 6, 0,
- X "unset", 5, 3,
- X "unshar", 6, 0, /* QUICK HACK */
- X NULL, 0, 0
- X};
- X
- Xalt_completion(buf, index)
- Xchar *buf;
- Xint index;
- X{
- X static char *head, *tail = NULL, buffer[FILENAME];
- X static int len;
- X static struct alt_commands *alt, *help_alt;
- X static fct_type other_compl;
- X int temp;
- X register char *p, *q;
- X extern int file_completion(), var_completion(), cmd_completion();
- X extern int list_offset;
- X
- X if (other_compl) {
- X temp = CALL(other_compl)(buf, index);
- X if (index == 0 && temp == 1 && tail) strcpy(tail, head);
- X if (index < 0 || (index == 0 && temp == 0)) {
- X other_compl = NULL;
- X list_offset = 0;
- X }
- X return temp;
- X }
- X
- X if (index < 0) return 0;
- X
- X if (buf) {
- X head = buf;
- X tail = buf + index;
- X alt = help_alt = alt_commands;
- X len = tail - head;
- X other_compl = NULL;
- X
- X for (; alt->alt_name; alt++) {
- X if (len <= alt->alt_len || head[alt->alt_len] != SP) continue;
- X index = strncmp(alt->alt_name, head, alt->alt_len);
- X if (index < 0) continue;
- X if (index > 0) break;
- X
- X if (alt->alt_type < 0) {
- X if (len > alt->alt_len) continue;
- X break;
- X }
- X
- X if (alt->alt_type == 0) return -1; /* cannot be further compl */
- X
- X head += alt->alt_len;
- X while (*head && *head == SP) head++;
- X len = tail - head;
- X temp = -1;
- X
- X switch (alt->alt_type) {
- X case 1:
- X other_compl = file_completion;
- X tail = NULL;
- X temp = file_completion(head, len);
- X break;
- X
- X case 2:
- X other_compl = file_completion;
- X sprintf(buffer, "%s.%s",
- X relative(help_directory, "help"), head);
- X len = strlen(buffer);
- X head = buffer + len;
- X list_offset = 5;
- X temp = file_completion(buffer, len);
- X break;
- X
- X case 3:
- X /* [set ]variable[ value] */
- X for (p = head; *p; )
- X if (*p++ == SP) return -1;
- X other_compl = var_completion;
- X tail = NULL;
- X temp = var_completion(head, len);
- X break;
- X
- X case 4:
- X /* [map XXX ]Y command[ N] */
- X for (p = head, temp = 0; *p; )
- X if (*p++ == SP) {
- X while (*p && *p == SP) p++;
- X head = p;
- X temp++;
- X }
- X if (temp != 1) return -1;
- X
- X other_compl = cmd_completion;
- X tail = NULL;
- X len = p - head;
- X temp = cmd_completion(head, len);
- X break;
- X }
- X if (temp <= 0) other_compl = NULL;
- X return temp;
- X }
- X
- X alt = alt_commands;
- X return 1;
- X }
- X
- X if (index) {
- X list_completion((char *)NULL);
- X if (help_alt->alt_name == NULL) help_alt = alt_commands;
- X list_offset = 0;
- X if (p = strrchr(head, ' ')) list_offset = p - head;
- X
- X while (help_alt->alt_name) {
- X if (len > help_alt->alt_len ||
- X (index = strncmp(help_alt->alt_name, head, len)) < 0) {
- X help_alt++;
- X continue;
- X }
- X if (index > 0) {
- X help_alt = alt_commands;
- X break;
- X }
- X p = help_alt->alt_name;
- X if (list_completion(p) == 0) break;
- X temp = help_alt->alt_len;
- X
- X do help_alt++;
- X while ((q = help_alt->alt_name) && help_alt->alt_len > temp &&
- X strncmp(p, q, temp) == 0);
- X }
- X fl;
- X list_offset = 0;
- X return 1;
- X }
- X
- X for (; alt->alt_name; alt++) {
- X if (len == 0)
- X index = 0;
- X else
- X index = strncmp(alt->alt_name, head, len);
- X if (index < 0) continue;
- X if (index > 0) break;
- X
- X p = alt->alt_name;
- X sprintf(tail, "%s ", p + len);
- X temp = alt->alt_len;
- X
- X do alt++;
- X while ((q = alt->alt_name) && alt->alt_len > temp &&
- X strncmp(p, q, temp) == 0);
- X
- X return 1;
- X }
- X return 0;
- X}
- X
- Xstatic print_debug_info()
- X{
- X clrdisp();
- X printf("group=%s, nart=%ld\n\r", current_group->group_name, n_articles);
- X any_key(0);
- X}
- X
- Xstatic print_command(str)
- Xchar *str;
- X{
- X char **av;
- X
- X if (!in_init) {
- X msg(str);
- X return;
- X }
- X
- X printf("\r%s:", str);
- X for (av = argvec; *av; av++)
- X printf(" %s", *av);
- X putchar(NL);
- X}
- X
- X
- Xstatic do_show(table, mode_arg)
- Xchar *table;
- Xint mode_arg;
- X{
- X register group_header *gh;
- X
- X if (in_init || table == NULL) return 0;
- X
- X no_raw();
- X
- X SWITCH( table ) {
- X
- X CASE( "kill" ) {
- X clrdisp();
- X dump_kill_list();
- X break;
- X }
- X
- X CASE( "groups" ) {
- X
- X clrdisp();
- X if ARG(mode_arg, "all")
- X group_overview(1);
- X else
- X if ARG(mode_arg, "total")
- X group_overview(2);
- X else
- X if ARG(mode_arg, "unsub")
- X group_overview(3);
- X else
- X group_overview(0);
- X
- X break;
- X }
- X
- X CASE( "map" ) {
- X char *name;
- X extern in_menu_mode;
- X
- X if ((name = argv(mode_arg)) == NULL)
- X name = in_menu_mode ? "menu" : "show";
- X
- X if (name[0] == '#') {
- X clrdisp();
- X dump_multi_keys();
- X break;
- X }
- X
- X SWITCH( name ) {
- X
- X CASE( "key" ) {
- X clrdisp();
- X dump_global_map();
- X break;
- X }
- X CASE( "menu" ) {
- X clrdisp();
- X dump_key_map(menu_key_map, "menu", K_ONLY_MENU);
- X break;
- X }
- X CASE( "show" ) {
- X clrdisp();
- X dump_key_map(more_key_map, "show", K_ONLY_MORE);
- X break;
- X }
- X
- X init_message("unknown map '%s'", argv(mode_arg));
- X goto err;
- X /*NOTREACHED*/
- X }
- X
- X break;
- X }
- X
- X CASE( "rc" ) {
- X if (argv(2)) {
- X gh = lookup(argv(2));
- X if (gh == NULL) {
- X msg("Unknown: %s", argv(2));
- X break;
- X }
- X } else
- X gh = current_group;
- X if (gh->group_flag & G_FAKED) break;
- X
- X clrdisp();
- X
- X printf("Available: %ld - %ld (unread %ld)\n\n",
- X (long)(gh->first_db_article), (long)(gh->last_db_article),
- X (long)(gh->unread_count));
- X printf(".newsrc:\n\r>%s\r<%s\n\rselect:\n\r>%s\r<%s\n\r",
- X gh->newsrc_line ? gh->newsrc_line : "(null)\n",
- X gh->newsrc_orig == gh->newsrc_line ? "(same)\n" :
- X gh->newsrc_orig ? gh->newsrc_orig : "(null)\n",
- X gh->select_line ? gh->select_line : "(null)\n",
- X gh->select_orig == gh->select_line ? "(same)\n" :
- X gh->select_orig ? gh->select_orig : "(null)\n");
- X any_key(0);
- X break;
- X }
- X
- X init_message("unknown table '%s'", table);
- X goto err;
- X /*NOTREACHED*/
- X }
- X
- X raw();
- X return 1;
- Xerr:
- X raw();
- X return 0;
- X}
- X
- X
- Xstatic do_map(initf)
- XFILE *initf;
- X{
- X int code, map_menu, map_show, must_redraw = 0;
- X
- X SWITCH( argv(1) ) {
- X
- X CASE( "key" ) {
- X if (argv(3) == NULL) break;
- X global_key_map[parse_key(argv(2))] = parse_key(argv(3));
- X goto out;
- X }
- X
- X if (argv(1)[0] == '#') {
- X key_type multi_buffer[16], *mb;
- X int i;
- X
- X if (!isdigit(argv(1)[1])) break;
- X
- X for (i = 2, mb = multi_buffer; argv(i); i++)
- X *mb++ = parse_key(argv(i));
- X *mb = NUL;
- X
- X enter_multi_key(K_function(argv(1)[1] - '0'),
- X (key_type *)copy_str((char *)multi_buffer));
- X
- X goto out;
- X }
- X
- X code = K_UNBOUND;
- X map_menu = map_show = 0;
- X
- X CASE( "menu" ) {
- X map_menu++;
- X }
- X CASE( "show" ) {
- X map_show++;
- X }
- X CASE( "both" ) {
- X map_menu++;
- X map_show++;
- X }
- X
- X if (ARG(3, "(")) {
- X extern char *m_define();
- X
- X code = (int)m_define("-2", initf);
- X must_redraw = 1;
- X if (code == K_UNBOUND) goto mac_err;
- X }
- X
- X if (map_menu) {
- X if (code == K_UNBOUND && argv(3))
- X code = lookup_command(argv(3), K_ONLY_MENU);
- X
- X if (code == K_EQUAL_KEY) {
- X if (argv(4))
- X code = menu_key_map[parse_key(argv(4))];
- X else
- X goto mac_err;
- X } else
- X if (code == K_MACRO || code == K_ARTICLE_ID)
- X if (argv(4))
- X code |= atoi(argv(4));
- X else
- X goto mac_err;
- X
- X if (code != K_INVALID) {
- X menu_key_map[parse_key(argv(2))] = code;
- X if (!map_show) goto out;
- X }
- X }
- X
- X if (map_show) {
- X if (code == K_UNBOUND && argv(3))
- X code = lookup_command(argv(3), K_ONLY_MORE);
- X
- X if (code == K_EQUAL_KEY) {
- X if (argv(4))
- X code = menu_key_map[parse_key(argv(4))];
- X else
- X goto mac_err;
- X } else
- X if (code == K_MACRO)
- X if (argv(4))
- X code |= atoi(argv(4));
- X else
- X goto mac_err;
- X
- X if (code != K_INVALID) {
- X more_key_map[parse_key(argv(2))] = code;
- X goto out;
- X }
- X }
- X
- X if (argv(4)) break;
- X
- X if (code == K_INVALID) {
- X init_message("unknown key command: %s", argv(3));
- X goto out;
- X }
- X }
- X
- X print_command("syntax error");
- X goto out;
- X
- X mac_err:
- X print_command("map argument missing");
- X out:
- X return must_redraw;
- X}
- X
- Xstatic parse_on_to_end(f)
- XFILE *f;
- X{
- X register char *cp;
- X char buf[256];
- X
- X if (argv(1) == NULL) goto on_err;
- X
- X SWITCH ( argv(1) ) {
- X
- X CASE( "entry" ) {
- X import char *dflt_enter_macro;
- X group_header *gh, *get_group_search();
- X char *macro, *parse_enter_macro();
- X int i;
- X
- X macro = parse_enter_macro(f, NL);
- X if (ARGTAIL) {
- X for (i = 2; argv(i); i++) {
- X start_group_search(argv(i));
- X while (gh = get_group_search())
- X gh->enter_macro = macro;
- X }
- X } else
- X dflt_enter_macro = macro;
- X return;
- X }
- X
- X/* CASE( "exit" ) {
- X import char *dflt_exit_macro;
- X
- X dflt_exit_macro = parse_enter_macro(f, NL);
- X return;
- X }
- X*/
- X CASE( "slow" ) {
- X import int terminal_speed, slow_speed;
- X
- X if (terminal_speed <= (slow_speed / 10)) return;
- X break;
- X }
- X
- X CASE( "fast" ) {
- X import int terminal_speed, slow_speed;
- X
- X if (terminal_speed > (slow_speed / 10)) return;
- X break;
- X }
- X
- X CASE( "term" ) {
- X extern char *term_name;
- X int i;
- X
- X for (i = 2; argv(i) != NULL; i++)
- X if (strcmp(argv(2), term_name) == 0) return;
- X break;
- X }
- X
- X CASE( "host" ) {
- X char local_host[100];
- X int i;
- X
- X gethostname(local_host, 100);
- X for (i = 2; argv(i) != NULL; i++)
- X if (strcmp(argv(2), local_host) == 0) return;
- X break;
- X }
- X
- X goto on_err;
- X }
- X
- X while (fgets(buf, 256, f) != NULL) {
- X for (cp = buf; *cp && isascii(*cp) && isspace(*cp); cp++);
- X if (strncmp(cp, "end", 3) == 0) return;
- X }
- X init_message("end missing (on %s)", argv(1));
- X return;
- X
- Xon_err:
- X init_message("on `what'?");
- X}
- X
- Xparse_command(cmd, ok_val, initf)
- Xchar *cmd;
- Xint ok_val;
- XFILE *initf;
- X{
- X extern char *m_define(), *parse_enter_macro();
- X
- X if (!split_command(cmd)) return ok_val;
- X
- X if (*ARGTAIL == '!') {
- X if (ok_val == AC_UNCHANGED) { /* in macro */
- X if (ARGTAIL[1] == '!') /* !!cmd => guarantee no output! */
- X run_shell(ARGTAIL+2, -2);
- X else
- X run_shell(ARGTAIL+1, -1);
- X return ok_val;
- X }
- X if (run_shell(ARGTAIL+1, ok_val == AC_PROMPT ? 1 : 0)) {
- X any_key(0);
- X return AC_REDRAW;
- X }
- X return ok_val;
- X }
- X
- X SWITCH( argv(0) ) {
- X
- X CASE( "unset" ) {
- X if (argv(1) == NULL) goto stx_err;
- X
- X if (set_variable(argv(1), 0, (char *)NULL))
- X return AC_REDRAW;
- X else
- X return ok_val;
- X }
- X
- X CASE( "local" ) {
- X if (ARGTAIL == NULL) goto stx_err;
- X
- X cmd = argv(1);
- X if (!push_variable(cmd)) return ok_val;
- X
- X if (ARGTAIL && set_variable(cmd, 1, ARGTAIL))
- X return AC_REDRAW;
- X else
- X return ok_val;
- X }
- X
- X CASE( "set" ) {
- X if (ARGTAIL == NULL) {
- X disp_variables(0);
- X return AC_REDRAW;
- X }
- X
- X cmd = argv(1); /* get ARGTAIL right */
- X if (cmd != NULL && strcmp(cmd, "all") == 0) {
- X disp_variables(1);
- X return AC_REDRAW;
- X }
- X
- X if (set_variable(cmd, 1, ARGTAIL))
- X return AC_REDRAW;
- X else
- X return ok_val;
- X }
- X
- X CASE( "toggle" ) {
- X if (argv(1) == NULL) goto stx_err;
- X toggle_variable(argv(1));
- X break;
- X }
- X
- X CASE( "define" ) {
- X if (in_init) {
- X if (argv(1) == NULL) {
- X init_message("macro number missing");
- X break;
- X }
- X m_define(argv(1), initf);
- X } else
- X if (m_define(argv(1), (FILE *)NULL))
- X return AC_REDRAW;
- X
- X break;
- X }
- X
- X CASE( "map" ) {
- X if (argv(2) == NULL) {
- X if (do_show("map", 1))
- X return AC_REDRAW;
- X break;
- X }
- X
- X if (do_map(initf))
- X return AC_REDRAW;
- X break;
- X }
- X
- X CASE( "cd" ) {
- X if (change_dir(argv(1), in_init))
- X init_message("chdir %s FAILED", argv(1));
- X
- X break;
- X }
- X
- X if (in_init) {
- X
- X CASE( "load" ) {
- X if (argv(1)) load_init_file(argv(1), (FILE **)NULL, 0);
- X break;
- X }
- X
- X CASE( "on" ) {
- X parse_on_to_end(initf);
- X break;
- X }
- X
- X CASE( "end" ) {
- X break;
- X }
- X
- X CASE( "sequence" ) {
- X return START_SEQUENCE;
- X }
- X
- X CASE( "save-files" ) {
- X parse_save_files(initf);
- X break;
- X }
- X
- X print_command("unknown command");
- X break;
- X }
- X
- X /*
- X * commands only available from : command line
- X */
- X
- X if (ok_val != AC_REDRAW) {
- X extern in_menu_mode;
- X
- X alt_cmd_key = lookup_command(sw_string,
- X in_menu_mode ? K_ONLY_MENU : K_ONLY_MORE);
- X if (alt_cmd_key != K_INVALID && alt_cmd_key != K_HELP)
- X return AC_KEYCMD;
- X }
- X
- X CASE( "q" ) {
- X break;
- X }
- X
- X CASE( "Q" ) {
- X return AC_QUIT;
- X }
- X
- X CASE( "q!" ) {
- X if (restore_bak())
- X return AC_QUIT;
- X break;
- X }
- X
- X CASE( "x" ) {
- X update_rc_all(current_group, 0);
- X return AC_QUIT;
- X }
- X
- X CASE( "help" ) {
- X if (argv(1) == NULL)
- X display_help("help");
- X else
- X display_help(argv(1));
- X return AC_REDRAW;
- X }
- X
- X CASE( "man" ) {
- X char *manual;
- X group_header *orig_group;
- X int orig_layout, orig_fsort;
- X import int fmt_linenum, dont_sort_folders;
- X
- X manual = relative(help_directory, "Manual");
- X if (!file_exist(manual, "fr")) {
- X manual = relative(db_directory, "Manual");
- X if (!file_exist(manual, "fr")) {
- X msg("Online manual is not available");
- X break;
- X }
- X }
- X orig_group = current_group;
- X orig_layout = fmt_linenum;
- X orig_fsort = dont_sort_folders;
- X
- X fmt_linenum = -1;
- X dont_sort_folders = 1;
- X
- X folder_menu(manual);
- X
- X fmt_linenum = orig_layout;
- X dont_sort_folders = orig_fsort;
- X init_group(orig_group);
- X
- X return AC_REDRAW;
- X }
- X
- X CASE( "sort" ) {
- X if (argv(1) == NULL)
- X sort_articles(-1);
- X else if (ARG(1, "no") || ARG(1, "arrival"))
- X sort_articles(0);
- X else if ARG(1, "subject")
- X sort_articles(1);
- X else if ARG(1, "lexical")
- X sort_articles(2);
- X else if (ARG(1, "date") || ARG(1, "age"))
- X sort_articles(3);
- X else if (ARG(1, "sender") || ARG(1, "from"))
- X sort_articles(4);
- X else {
- X msg("Unknown sort mode '%s'", argv(1));
- X break;
- X }
- X
- X return AC_REORDER;
- X }
- X
- X CASE( "unread" ) {
- X group_header *gh;
- X int ix;
- X int32 restore_rc();
- X
- X if (argv(1) && (gh = lookup(argv(1))) != NULL)
- X ix = 2;
- X else {
- X ix = 1;
- X gh = current_group;
- X }
- X
- X if (gh == current_group) return AC_REENTER_GROUP;
- X
- X if (argv(ix)) {
- X if (!restore_rc(gh, gh->last_db_article - ARGVAL(ix)))
- X break;
- X } else
- X if (!restore_unread(gh))
- X break;
- X break;
- X }
- X
- X CASE( "dump" ) {
- X if (do_show(argv(1), 2))
- X return AC_REDRAW;
- X break;
- X }
- X
- X CASE( "show" ) {
- X if (do_show(argv(1), 2))
- X return AC_REDRAW;
- X break;
- X }
- X
- X CASE( "compile" ) {
- X import int do_kill_handling;
- X
- X clrdisp();
- X rm_kill_file();
- X free_kill_entries();
- X do_kill_handling = init_kill() && do_kill_handling;
- X return AC_REDRAW;
- X }
- X
- X CASE( "pwd" ) {
- X FILE *p = popen("exec pwd", "r");
- X char dir[FILENAME];
- X if (p) {
- X if (fgets(dir, FILENAME, p)) {
- X dir[strlen(dir) - 1] = NUL;
- X msg("%s", dir);
- X }
- X pclose(p);
- X }
- X break;
- X }
- X
- X CASE( "rmail" ) {
- X import char *mail_box;
- X group_header *orig_group;
- X
- X if (mail_box == NULL) {
- X msg("'mail' path not defined");
- X break;
- X }
- X
- X orig_group = current_group;
- X folder_menu(mail_box);
- X init_group(orig_group);
- X
- X return AC_REDRAW;
- X }
- X
- X CASE( "mkdir" ) {
- X char *dir, *run_mkdir();
- X char name_buf[FILENAME];
- X
- X if (dir = run_mkdir(argv(1), name_buf)) {
- X prompt("Change to %s", dir);
- X if (yes(0)) change_dir(dir, 0);
- X }
- X break;
- X }
- X
- X CASE( "sh" ) {
- X suspend_nn();
- X s_redraw = 0;
- X return AC_REDRAW;
- X }
- X
- X CASE( "admin" ) {
- X group_header *cur_group;
- X
- X cur_group = current_group;
- X no_raw();
- X clrdisp();
- X printf("\n\n\n\rADMINISTRATION MODE\r\n\n\n");
- X admin_mode((char *)NULL);
- X clrdisp();
- X raw();
- X init_group(cur_group);
- X return AC_REDRAW;
- X }
- X
- X CASE( "cost" ) {
- X#ifdef ACCOUNTING
- X gotoxy(0, Lines-1); clrline();
- X account('C', 1);
- X#else
- X msg("No accounting");
- X#endif
- X break;
- X }
- X
- X CASE( "bug" ) {
- X if (answer((article_header *)NULL, K_BUG_REPORT, 0))
- X return AC_REDRAW;
- X break;
- X }
- X
- X CASE( "debug" ) {
- X print_debug_info();
- X return AC_REDRAW;
- X }
- X
- X CASE( "coredump" ) {
- X unset_raw();
- X abort();
- X }
- X
- X msg("unknown command: \"%s\"", argv(0));
- X }
- X
- X return ok_val;
- X
- X stx_err:
- X print_command("syntax error");
- X return ok_val;
- X}
- X
- X
- Xdisplay_help(subject)
- Xchar *subject;
- X{
- X char file[FILENAME];
- X
- X strcpy(file, "help.");
- X strcpy(file+5, subject);
- X
- X display_file(file, CLEAR_DISPLAY | CONFIRMATION);
- X}
- END_OF_FILE
- if test 21451 -ne `wc -c <'init.c'`; then
- echo shar: \"'init.c'\" unpacked with wrong size!
- fi
- # end of 'init.c'
- fi
- if test -f 'kill.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'kill.c'\"
- else
- echo shar: Extracting \"'kill.c'\" \(21836 characters\)
- sed "s/^X//" >'kill.c' <<'END_OF_FILE'
- X/*
- X * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
- X *
- X * Kill file handling
- X */
- X
- X#include "config.h"
- X#include "term.h"
- X#include "regexp.h"
- X
- Xexport int killed_articles;
- X
- Xchar KILL_FILE[] = "kill";
- Xchar COMPILED_KILL[] = "KILL.COMP";
- X
- X#define COMP_KILL_MAGIC 0x4b694c6f /* KiLo */
- X
- X/*
- X * kill flags
- X */
- X
- X#define COMP_KILL_ENTRY 0x80000000
- X
- X#define GROUP_REGEXP 0x01000000
- X#define GROUP_REGEXP_HDR 0x02000000
- X
- X#define AND_MATCH 0x00020000
- X#define OR_MATCH 0x00010000
- X
- X#define KILL_CASE_MATCH 0x00000100
- X#define KILL_ON_REGEXP 0x00000200
- X#define KILL_UNLESS_MATCH 0x00000400
- X
- X#define AUTO_KILL 0x00000001
- X#define AUTO_SELECT 0x00000002
- X#define ON_SUBJECT 0x00000004
- X#define ON_SENDER 0x00000008
- X#define ON_FOLLOW_UP 0x00000010
- X#define ON_ANY_REFERENCES 0x00000020
- X
- X/*
- X * external flag representation
- X */
- X
- X#define EXT_AUTO_KILL '!'
- X#define EXT_AUTO_SELECT '+'
- X#define EXT_KILL_UNLESS_MATCH '~'
- X#define EXT_ON_FOLLOW_UP '>'
- X#define EXT_ON_ANY_REFERENCES 'a'
- X#define EXT_ON_SUBJECT 's'
- X#define EXT_ON_SENDER 'n'
- X#define EXT_KILL_CASE_MATCH '='
- X#define EXT_KILL_ON_REGEXP '/'
- X#define EXT_AND_MATCH '&'
- X#define EXT_OR_MATCH '|'
- X
- X/*
- X * period = nnn DAYS
- X */
- X
- X#define DAYS * 24 * 60 * 60
- X
- X
- X/*
- X * kill_article
- X *
- X * return 1 to kill article, 0 to include it
- X */
- X
- Xtypedef struct kill_list_entry {
- X flag_type kill_flag;
- X char *kill_pattern;
- X regexp *kill_regexp;
- X struct kill_list_entry *next_kill;
- X} kill_list_entry;
- X
- Xstatic kill_list_entry *kill_tab;
- Xstatic char *kill_patterns;
- X
- Xstatic kill_list_entry *global_kill_list = NULL;
- Xstatic kill_list_entry latest_kl_entry;
- X
- Xtypedef struct {
- X regexp *group_regexp;
- X kill_list_entry *kill_entry;
- X} kill_group_regexp;
- X
- Xstatic kill_group_regexp *group_regexp_table = NULL;
- Xstatic int regexp_table_size = 0;
- Xstatic kill_list_entry *regexp_kill_list = NULL;
- Xstatic group_header *current_kill_group = NULL;
- X
- X/*
- X * Build regexp_kill_list for current_group
- X */
- X
- Xstatic build_regexp_kill()
- X{
- X register kill_group_regexp *tb;
- X register int n, used_last;
- X register char *name;
- X
- X regexp_kill_list = NULL;
- X current_kill_group = current_group;
- X name = current_group->group_name;
- X used_last = 0; /* get AND_MATCH/OR_MATCH for free */
- X
- X for (n = regexp_table_size, tb = group_regexp_table; --n >= 0; tb++) {
- X if (tb->group_regexp != NULL) {
- X used_last = 0;
- X if (!regexec(tb->group_regexp, name)) continue;
- X } else
- X if (!used_last) continue;
- X
- X tb->kill_entry->next_kill = regexp_kill_list;
- X regexp_kill_list = tb->kill_entry;
- X used_last = 1;
- X }
- X}
- X
- X/*
- X * execute kill patterns on article
- X */
- X
- Xstatic kill_list_entry *exec_kill(kl, ah, unlessp, do_kill, do_select)
- Xregister kill_list_entry *kl;
- Xregister article_header *ah;
- Xint *unlessp, do_kill, do_select;
- X{
- X register flag_type flag;
- X register char *string;
- X
- X for ( ; kl != NULL; kl = kl->next_kill) {
- X flag = kl->kill_flag;
- X
- X if (do_select && (flag & AUTO_SELECT) == 0) goto failed;
- X if (do_kill && (flag & AUTO_KILL) == 0) goto failed;
- X
- X if (flag & KILL_UNLESS_MATCH)
- X *unlessp = 1;
- X
- X if (flag & ON_ANY_REFERENCES) {
- X if (ah->replies & 0x7f) goto match;
- X goto failed;
- X }
- X
- X if (flag & ON_SUBJECT) {
- X if (flag & ON_FOLLOW_UP) {
- X if ((ah->replies & 0x80) == 0) goto failed;
- X }
- X string = ah->subject;
- X } else
- X string = ah->sender;
- X
- X if (flag & KILL_CASE_MATCH) {
- X if (flag & KILL_ON_REGEXP) {
- X if (regexec(kl->kill_regexp, string)) goto match;
- X } else
- X if (strcmp(kl->kill_pattern, string) == 0) goto match;
- X } else
- X if (flag & KILL_ON_REGEXP) {
- X if (regexec_fold(kl->kill_regexp, string)) goto match;
- X } else
- X if (strmatch_fold(kl->kill_pattern, string)) goto match;
- X
- X failed:
- X if ((flag & AND_MATCH) == 0) continue;
- X
- X do /* skip next */
- X kl = kl->next_kill;
- X while (kl && (kl->kill_flag & AND_MATCH));
- X if (kl) continue;
- X break;
- X
- X match:
- X if (flag & AND_MATCH) continue;
- X break;
- X }
- X return kl;
- X}
- X
- X
- Xkill_article(ah)
- Xarticle_header *ah;
- X{
- X register kill_list_entry *kl;
- X int unless_match = 0;
- X
- X kl = exec_kill((kill_list_entry *)(current_group->kill_list), ah,
- X &unless_match, 0, 0);
- X if (kl == NULL && group_regexp_table != NULL) {
- X if (current_kill_group != current_group) build_regexp_kill();
- X kl = exec_kill(regexp_kill_list, ah, &unless_match, 0, 0);
- X }
- X if (kl == NULL)
- X kl = exec_kill(global_kill_list, ah, &unless_match, 0, 0);
- X
- X if (kl != NULL) {
- X if (kl->kill_flag & AUTO_KILL) {
- X killed_articles++;
- X return 1;
- X }
- X
- X if (kl->kill_flag & AUTO_SELECT)
- X ah->attr = A_AUTO_SELECT;
- X return 0;
- X }
- X
- X if (unless_match) {
- X killed_articles++;
- X return 1;
- X }
- X
- X return 0;
- X}
- X
- X
- Xauto_select_article(ah, do_select)
- Xarticle_header *ah;
- Xint do_select;
- X{
- X register kill_list_entry *kl;
- X int dummy;
- X
- X if (do_select == 1) {
- X kl = ah->a_group ? (kill_list_entry *)(ah->a_group->kill_list) :
- X (kill_list_entry *)(current_group->kill_list);
- X kl = exec_kill(kl, ah, &dummy, !do_select, do_select);
- X if (kl == NULL && group_regexp_table != NULL) {
- X if (current_kill_group != current_group) build_regexp_kill();
- X kl = exec_kill(regexp_kill_list, ah, &dummy, !do_select, do_select);
- X }
- X if (kl == NULL)
- X kl = exec_kill(global_kill_list, ah, &dummy, !do_select, do_select);
- X } else {
- X kl = exec_kill(&latest_kl_entry, ah, &dummy, !do_select, do_select);
- X }
- X
- X if (kl == NULL) return 0;
- X
- X if (!do_select) killed_articles++;
- X return 1;
- X}
- X
- X
- Xstatic fput_pattern(p, f)
- Xregister char *p;
- Xregister FILE *f;
- X{
- X register char c;
- X
- X while (c = *p++) {
- X if (c == ':' || c == '\\') putc('\\', f);
- X putc(c, f);
- X }
- X}
- X
- Xstatic char *get_pattern(p, lenp, more)
- Xregister char *p;
- Xint *lenp, more;
- X{
- X register char c, *q, *start;
- X
- X start = q = p;
- X while (c = *p++) {
- X if (c == '\\') {
- X c = *p++;
- X if (c != ':' && c != '\\') *q++ = '\\';
- X *q++ = c;
- X continue;
- X }
- X if (more) {
- X if (c == ':') break;
- X if (c == NL) return NULL;
- X } else
- X if (c == NL) break;
- X
- X *q++ = c;
- X }
- X
- X if (c == NUL) return NULL;
- X
- X *q++ = NUL;
- X *lenp = q - start;
- X return p;
- X}
- X
- Xenter_kill_file(gh, pattern, flag, days)
- Xgroup_header *gh;
- Xchar *pattern;
- Xregister flag_type flag;
- Xint days;
- X{
- X FILE *killf;
- X register kill_list_entry *kl;
- X regexp *re;
- X char *str;
- X
- X str = copy_str(pattern);
- X
- X if ((flag & KILL_CASE_MATCH) == 0)
- X fold_string(str);
- X
- X if (flag & KILL_ON_REGEXP) {
- X re = regcomp(pattern);
- X if (re == NULL) return;
- X } else
- X re = NULL;
- X
- X killf = open_file(relative(nn_directory, "kill"), OPEN_APPEND);
- X if (killf == NULL) {
- X msg("cannot create kill file");
- X return;
- X }
- X
- X if (days >= 0) {
- X if (days == 0) days = 30;
- X fprintf(killf, "%lu:", (long)(cur_time() + days DAYS));
- X }
- X
- X if (gh) fputs(gh->group_name, killf);
- X fputc(':', killf);
- X
- X if (flag & KILL_UNLESS_MATCH) fputc(EXT_KILL_UNLESS_MATCH, killf);
- X if (flag & AUTO_KILL) fputc(EXT_AUTO_KILL, killf);
- X if (flag & AUTO_SELECT) fputc(EXT_AUTO_SELECT, killf);
- X if (flag & ON_FOLLOW_UP) fputc(EXT_ON_FOLLOW_UP, killf);
- X if (flag & ON_ANY_REFERENCES) fputc(EXT_ON_ANY_REFERENCES, killf);
- X if (flag & ON_SENDER) fputc(EXT_ON_SENDER, killf);
- X if (flag & ON_SUBJECT) fputc(EXT_ON_SUBJECT, killf);
- X if (flag & KILL_CASE_MATCH) fputc(EXT_KILL_CASE_MATCH, killf);
- X if (flag & KILL_ON_REGEXP) fputc(EXT_KILL_ON_REGEXP, killf);
- X fputc(':', killf);
- X
- X fput_pattern(pattern, killf);
- X fputc(NL, killf);
- X
- X fclose(killf);
- X rm_kill_file();
- X
- X kl = newobj(kill_list_entry, 1);
- X
- X latest_kl_entry.kill_pattern = kl->kill_pattern = str;
- X latest_kl_entry.kill_regexp = kl->kill_regexp = re;
- X latest_kl_entry.kill_flag = kl->kill_flag = flag;
- X latest_kl_entry.next_kill = NULL;
- X
- X if (gh) {
- X kl->next_kill = (kill_list_entry *)(gh->kill_list);
- X gh->kill_list = (char *)kl;
- X } else {
- X kl->next_kill = global_kill_list;
- X global_kill_list = kl;
- X }
- X}
- X
- X
- Xtypedef struct {
- X group_number ck_group;
- X flag_type ck_flag;
- X long ck_pattern_index;
- X} comp_kill_entry;
- X
- Xtypedef struct {
- X long ckh_magic;
- X time_t ckh_db_check;
- X off_t ckh_pattern_offset;
- X long ckh_pattern_size;
- X long ckh_entries;
- X long ckh_regexp_size;
- X} comp_kill_header;
- X
- X
- Xkill_menu(ah)
- Xarticle_header *ah;
- X{
- X int days;
- X register flag_type flag;
- X char *mode1, *mode2;
- X char *pattern, *dflt, *days_str, buffer[512];
- X extern article_header *get_menu_article();
- X group_header *gh;
- X
- X prompt("\1AUTO\1 (k)ill or (s)elect (CR => Kill subject 1 month) ");
- X switch (get_c()) {
- X case CR:
- X case NL:
- X if (ah == NULL) {
- X ah = get_menu_article();
- X if (ah == NULL) return -1;
- X }
- X
- X strcpy(buffer, ah->subject);
- X enter_kill_file(current_group, buffer,
- X AUTO_KILL | ON_SUBJECT | KILL_CASE_MATCH, 30);
- X msg("DONE");
- X return 1;
- X
- X case 'k':
- X case 'K':
- X case '!':
- X flag = AUTO_KILL;
- X mode1 = "KILL";
- X break;
- X case 's':
- X case 'S':
- X case '+':
- X flag = AUTO_SELECT;
- X mode1 = "SELECT";
- X break;
- X default:
- X return -1;
- X }
- X
- X prompt("\1AUTO %s\1 on (s)ubject or (n)ame (s)", mode1);
- X
- X dflt = NULL;
- X switch (get_c()) {
- X case 'n':
- X case 'N':
- X flag |= ON_SENDER;
- X if (ah) dflt = ah->sender;
- X mode2 = "Name";
- X break;
- X case 's':
- X case 'S':
- X case SP:
- X case CR:
- X case NL:
- X flag |= ON_SUBJECT;
- X if (ah) dflt = ah->subject;
- X mode2 = "Subject";
- X break;
- X default:
- X return -1;
- X }
- X
- X prompt("\1%s %s:\1 (%=/) ", mode1, mode2);
- X
- X pattern = get_s(dflt, NONE, "%=/", NULL_FCT);
- X if (pattern == NULL) return -1;
- X if (*pattern == NUL || *pattern == '%' || *pattern == '=') {
- X if (dflt && *dflt)
- X pattern = dflt;
- X else {
- X if ((ah = get_menu_article()) == NULL) return -1;
- X pattern = (flag & ON_SUBJECT) ? ah->subject : ah->sender;
- X }
- X flag |= KILL_CASE_MATCH;
- X } else
- X if (*pattern == '/') {
- X prompt("\1%s %s\1 (regexp): ", mode1, mode2);
- X
- X pattern = get_s(NONE, NONE, NONE, NULL_FCT);
- X if (pattern == NULL || *pattern == NUL) return -1;
- X flag |= KILL_ON_REGEXP;
- X }
- X
- X strcpy(buffer, pattern);
- X pattern = buffer;
- X
- X prompt("\1%s\1 in (g)roup '%s' or in (a)ll groups (g)",
- X mode1, current_group->group_name);
- X
- X switch (get_c()) {
- X case 'g':
- X case 'G':
- X case SP:
- X case CR:
- X case NL:
- X gh = current_group;
- X break;
- X case 'A':
- X case 'a':
- X gh = NULL;
- X break;
- X default:
- X return -1;
- X }
- X
- X prompt("\1Lifetime of entry in days\1 (p)ermanent (30) ");
- X days_str = get_s(" 30 days", NONE, "pP", NULL_FCT);
- X if (days_str == NULL) return -1;
- X
- X if (*days_str == NUL) {
- X days_str = "30 days";
- X days = 30;
- X } else if (*days_str == 'p' || *days_str == 'P') {
- X days_str = "perm";
- X days = -1;
- X } else if (isdigit(*days_str)) {
- X days = atoi(days_str);
- X sprintf(days_str, "%d days", days);
- X } else {
- X ding();
- X return -1;
- X }
- X
- X prompt("\1CONFIRM\1 %s %s %s%s: %-.35s%s ",
- X mode1, mode2, days_str,
- X (flag & KILL_CASE_MATCH) ? " exact" :
- X (flag & KILL_ON_REGEXP) ? " regexp" : "",
- X pattern, strlen(pattern) > 35 ? "..." : "");
- X if (yes(0) <= 0) return -1;
- X
- X enter_kill_file(gh, pattern, flag, days);
- X
- X return (flag & AUTO_KILL) ? 1 : 0;
- X}
- X
- Xstatic compile_kill_file()
- X{
- X FILE *killf, *compf, *patternf, *dropf;
- X comp_kill_header header;
- X comp_kill_entry entry;
- X time_t now, age;
- X off_t cur_line_start;
- X char line[512];
- X register char *cp, *np;
- X register int c;
- X group_header *gh;
- X flag_type flag, fields[10];
- X extern char *temp_file;
- X int any_errors, nfield, nf, len;
- X
- X any_errors = 0;
- X header.ckh_entries = header.ckh_regexp_size = 0;
- X
- X killf = open_file(relative(nn_directory, KILL_FILE),
- X OPEN_READ | DONT_CREATE);
- X if (killf == NULL) return 0;
- X
- X compf = open_file(relative(nn_directory, COMPILED_KILL), OPEN_CREATE);
- X if (compf == NULL) goto err1;
- X
- X new_temp_file();
- X if ((patternf = open_file(temp_file, OPEN_CREATE)) == NULL)
- X goto err2;
- X
- X dropf = NULL;
- X
- X printf("\nCompiling kill file\n");
- X
- X fseek(compf, (off_t)sizeof(header), 0);
- X
- X now = cur_time();
- X
- X next_entry:
- X
- X for (;;) {
- X cur_line_start = ftell(killf);
- X
- X if (fgets(line, 512, killf) == NULL) break;
- X
- X cp = line;
- X while (*cp && isascii(*cp) && isspace(*cp)) cp++;
- X if (*cp == NUL || *cp == '#' || !isascii(*cp)) continue;
- X
- X if ((np = strchr(cp, ':')) == NULL) goto bad_entry;
- X
- X /* optional "age:" */
- X
- X if (np != cp && isdigit(*cp)) {
- X *np++ = NUL;
- X age = (time_t)atol(cp);
- X if (age < now) goto drop_entry;
- X cp = np;
- X if ((np = strchr(cp, ':')) == NULL) goto bad_entry;
- X }
- X
- X /* "group-name:" or "/regexp:" or ":" for all groups */
- X
- X flag = COMP_KILL_ENTRY;
- X
- X if (np == cp) {
- X entry.ck_group = -1;
- X np++;
- X } else {
- X *np++ = NUL;
- X if (*cp == '/') {
- X entry.ck_group = (long)ftell(patternf);
- X cp++;
- X len = strlen(cp) + 1;
- X if (fwrite(cp, sizeof(char), len, patternf) != len)
- X goto err3;
- X flag |= GROUP_REGEXP | GROUP_REGEXP_HDR ;
- X header.ckh_regexp_size++;
- X } else {
- X if ((gh = lookup(cp)) == NULL) {
- X printf("Unknown group in kill file: %s\n", cp);
- X any_errors++;
- X goto drop_entry;
- X }
- X entry.ck_group = gh->group_num;
- X }
- X }
- X
- X /* flags */
- X
- X cp = np;
- X nfield = 0;
- X
- X for (;;) {
- X switch (*cp++) {
- X case EXT_AND_MATCH:
- X case EXT_OR_MATCH:
- X fields[nfield++] = flag;
- X flag &= ~(AND_MATCH | ON_SUBJECT | ON_SENDER |
- X KILL_CASE_MATCH | KILL_ON_REGEXP |
- X GROUP_REGEXP_HDR);
- X flag |= (cp[-1] == EXT_AND_MATCH) ? AND_MATCH : OR_MATCH;
- X continue;
- X case EXT_AUTO_KILL:
- X flag |= AUTO_KILL;
- X continue;
- X case EXT_AUTO_SELECT:
- X flag |= AUTO_SELECT;
- X continue;
- X case EXT_ON_FOLLOW_UP:
- X flag |= ON_FOLLOW_UP;
- X continue;
- X case EXT_ON_ANY_REFERENCES:
- X flag |= ON_ANY_REFERENCES;
- X continue;
- X case EXT_ON_SUBJECT:
- X flag |= ON_SUBJECT;
- X continue;
- X case EXT_ON_SENDER:
- X flag |= ON_SENDER;
- X continue;
- X case EXT_KILL_CASE_MATCH:
- X flag |= KILL_CASE_MATCH;
- X continue;
- X case EXT_KILL_UNLESS_MATCH:
- X flag |= KILL_UNLESS_MATCH;
- X continue;
- X case EXT_KILL_ON_REGEXP:
- X flag |= KILL_ON_REGEXP;
- X continue;
- X case ':':
- X break;
- X case NL:
- X goto bad_entry;
- X default:
- X printf("Ignored flag '%c' in kill file\n", cp[-1]);
- X any_errors++;
- X continue;
- X }
- X break;
- X }
- X
- X fields[nfield++] = flag;
- X
- X for (nf = 0; --nfield >= 0; nf++) {
- X entry.ck_flag = flag = fields[nf];
- X np = cp;
- X if ((cp = get_pattern(np, &len, nfield)) == NULL) goto bad_entry;
- X
- X if ((flag & KILL_CASE_MATCH) == 0)
- X fold_string(np);
- X
- X entry.ck_pattern_index = ftell(patternf);
- X
- X if (fwrite((char *)&entry, sizeof(entry), 1, compf) != 1)
- X goto err3;
- X
- X if (fwrite(np, sizeof(char), len, patternf) != len)
- X goto err3;
- X
- X header.ckh_entries++;
- X }
- X }
- X
- X header.ckh_pattern_size = ftell(patternf);
- X
- X fclose(patternf);
- X patternf = open_file(temp_file, OPEN_READ | OPEN_UNLINK);
- X if (patternf == NULL) goto err2;
- X
- X header.ckh_pattern_offset = ftell(compf);
- X
- X while ((c = getc(patternf)) != EOF)
- X putc(c, compf);
- X
- X fclose(patternf);
- X
- X rewind(compf);
- X
- X header.ckh_magic = COMP_KILL_MAGIC;
- X header.ckh_db_check = master.db_created;
- X
- X if (fwrite((char *)&header, sizeof(header), 1, compf) != 1)
- X goto err2;
- X
- X fclose(compf);
- X fclose(killf);
- X if (dropf != NULL) fclose(dropf);
- X
- X if (any_errors) {
- X putchar(NL);
- X any_key(0);
- X }
- X
- X return 1;
- X
- X bad_entry:
- X printf("Incomplete kill file entry:\n%s", line);
- X fl;
- X any_errors++;
- X
- X drop_entry:
- X if (dropf == NULL) {
- X dropf = open_file(relative(nn_directory, KILL_FILE),
- X OPEN_UPDATE | DONT_CREATE);
- X if (dropf == NULL) goto next_entry;
- X }
- X fseek(dropf, cur_line_start, 0);
- X fwrite("# ", sizeof(char), 2, dropf);
- X goto next_entry;
- X
- X err3:
- X fclose(patternf);
- X unlink(temp_file);
- X err2:
- X fclose(compf);
- X rm_kill_file();
- X err1:
- X fclose(killf);
- X if (dropf != NULL) fclose(dropf);
- X
- X msg("cannot compile kill file");
- X return 0;
- X}
- X
- Xinit_kill()
- X{
- X FILE *killf;
- X comp_kill_header header;
- X comp_kill_entry entry;
- X register kill_list_entry *kl;
- X register kill_group_regexp *tb;
- X register group_header *gh;
- X time_t kill_age, comp_age;
- X register long n;
- X int first_try = 1;
- X import char delayed_msg[];
- X
- X Loop_Groups_Header(gh)
- X gh->kill_list = NULL;
- X
- X kill_age = file_exist(relative(nn_directory, KILL_FILE), "frw");
- X if (kill_age == 0) return 0;
- X
- X comp_age = file_exist(relative(nn_directory, COMPILED_KILL), "fr");
- X again:
- X if (comp_age < kill_age && !compile_kill_file()) return 0;
- X
- X kill_tab = NULL;
- X kill_patterns = NULL;
- X group_regexp_table = NULL;
- X regexp_table_size = 0;
- X
- X killf = open_file(relative(nn_directory, COMPILED_KILL), OPEN_READ);
- X if (killf == NULL) return 0;
- X
- X if (fread((char *)&header, sizeof(header), 1, killf) != 1) goto err;
- X /* MAGIC check: format changed or using different hardware */
- X if (header.ckh_magic != COMP_KILL_MAGIC) goto err;
- X /* DB check: if database is rebuilt, group numbers may change */
- X if (header.ckh_db_check != master.db_created) goto err;
- X
- X kill_patterns = newstr(header.ckh_pattern_size);
- X kill_tab = newobj(kill_list_entry, header.ckh_entries);
- X if (regexp_table_size = header.ckh_regexp_size)
- X group_regexp_table = newobj(kill_group_regexp, header.ckh_regexp_size);
- X
- X fseek(killf, (off_t)(header.ckh_entries * sizeof(entry)), 1);
- X if (fread(kill_patterns, sizeof(char), (int)header.ckh_pattern_size, killf)
- X != header.ckh_pattern_size) goto err;
- X
- X tb = group_regexp_table;
- X
- X fseek(killf, (off_t)sizeof(header), 0);
- X for (n = header.ckh_entries, kl = kill_tab; --n >= 0; kl++) {
- X if (fread((char *)&entry, sizeof(entry), 1, killf) != 1) goto err;
- X if (header.ckh_pattern_size <= entry.ck_pattern_index ||
- X entry.ck_pattern_index < 0) goto err;
- X
- X kl->kill_pattern = kill_patterns + entry.ck_pattern_index;
- X kl->kill_flag = entry.ck_flag;
- X
- X if (kl->kill_flag & KILL_ON_REGEXP)
- X kl->kill_regexp = regcomp(kl->kill_pattern);
- X else
- X kl->kill_regexp = NULL;
- X
- X if (kl->kill_flag & GROUP_REGEXP) {
- X if (kl->kill_flag & GROUP_REGEXP_HDR) {
- X if (header.ckh_pattern_size <= entry.ck_group ||
- X entry.ck_group < 0) goto err;
- X tb->group_regexp = regcomp(kill_patterns + entry.ck_group);
- X } else
- X tb->group_regexp = NULL;
- X tb->kill_entry = kl;
- X tb++;
- X } else
- X if (entry.ck_group >= 0) {
- X gh = active_groups + entry.ck_group;
- X kl->next_kill = (kill_list_entry *)(gh->kill_list);
- X gh->kill_list = (char *)kl;
- X } else {
- X kl->next_kill = global_kill_list;
- X global_kill_list = kl;
- X }
- X }
- X
- X fclose(killf);
- X
- X return 1;
- X
- X err:
- X if (group_regexp_table != NULL) freeobj(group_regexp_table);
- X if (kill_patterns != NULL) freeobj(kill_patterns);
- X if (kill_tab != NULL) freeobj(kill_tab);
- X
- X fclose(killf);
- X rm_kill_file();
- X if (first_try) {
- X first_try = 0;
- X comp_age = 0;
- X goto again;
- X }
- X
- X strcpy(delayed_msg, "Error in compiled kill file (ignored)");
- X
- X Loop_Groups_Header(gh)
- X gh->kill_list = NULL;
- X
- X global_kill_list = NULL;
- X group_regexp_table = NULL;
- X
- X return 0;
- X}
- X
- X
- X
- Xrm_kill_file()
- X{
- X unlink(relative(nn_directory, COMPILED_KILL));
- X}
- X
- X
- Xstatic free_kill_list(kl)
- Xregister kill_list_entry *kl;
- X{
- X register kill_list_entry *nxt;
- X while (kl) {
- X nxt = kl->next_kill;
- X if (kl->kill_regexp != NULL) freeobj(kl->kill_regexp);
- X if ((kl->kill_flag & COMP_KILL_ENTRY) == 0) {
- X if (kl->kill_pattern != NULL) freeobj(kl->kill_pattern);
- X freeobj(kl);
- X }
- X kl = nxt;
- X }
- X}
- X
- Xfree_kill_entries()
- X{
- X register group_header *gh;
- X register kill_group_regexp *tb;
- X register int n;
- X
- X Loop_Groups_Header(gh)
- X if (gh->kill_list) {
- X free_kill_list((kill_list_entry *)(gh->kill_list));
- X gh->kill_list = NULL;
- X }
- X
- X free_kill_list(global_kill_list);
- X global_kill_list = NULL;
- X
- X if (tb = group_regexp_table) {
- X for (n = regexp_table_size; --n >= 0; tb++)
- X if (tb->group_regexp != NULL) freeobj(tb->group_regexp);
- X
- X freeobj(group_regexp_table);
- X group_regexp_table = NULL;
- X }
- X
- X if (kill_patterns != NULL) freeobj(kill_patterns);
- X if (kill_tab != NULL) freeobj(kill_tab);
- X}
- X
- X
- Xstatic flag_type pk_prev_and;
- X
- Xstatic print_kill(kl)
- Xregister kill_list_entry *kl;
- X{
- X register flag_type flag = kl->kill_flag;
- X
- X if (pg_next() < 0) return -1;
- X
- X if (pk_prev_and)
- X printf("\r AND ");
- X else
- X printf("\r%s%s ON ",
- X flag & AUTO_KILL ? "AUTO KILL" :
- X flag & AUTO_SELECT ? "AUTO SELECT" : "",
- X
- X (flag & KILL_UNLESS_MATCH) == 0 ? "" :
- X flag & AUTO_KILL ? " UNLESS" :
- X flag & AUTO_SELECT ? "" : "KEEP");
- X
- X printf("%s '%.35s'%s\n",
- X flag & ON_SUBJECT ? "SUBJECT" :
- X flag & ON_SENDER ? "NAME" : "????",
- X
- X kl->kill_pattern,
- X
- X flag & KILL_CASE_MATCH ?
- X (flag & KILL_ON_REGEXP ? " (re exact)" : " (exact)") :
- X (flag & KILL_ON_REGEXP ? " (re fold)" : ""));
- X
- X pk_prev_and = flag & AND_MATCH;
- X
- X return 0;
- X}
- X
- Xdump_kill_list()
- X{
- X register kill_list_entry *kl;
- X
- X pg_init(0, 1);
- X pg_next();
- X
- X kl = (kill_list_entry *)(current_group->kill_list);
- X if (current_kill_group != current_group) build_regexp_kill();
- X
- X if (kl == NULL && regexp_kill_list == NULL) {
- X printf("No kill entries for %s", current_group->group_name);
- X } else {
- X so_printf("\1GROUP %s kill list entries\1", current_group->group_name);
- X
- X pk_prev_and = 0;
- X for ( ; kl; kl = kl->next_kill)
- X if (print_kill(kl) < 0) goto out;
- X
- X pk_prev_and = 0;
- X for (kl = regexp_kill_list ; kl; kl = kl->next_kill)
- X if (print_kill(kl) < 0) goto out;
- X
- X if (pg_next() < 0) goto out;
- X }
- X
- X if (pg_next() < 0) goto out;
- X so_printf("\1GLOBAL kill list entries:\1");
- X
- X pk_prev_and = 0;
- X for (kl = global_kill_list; kl != NULL; kl = kl->next_kill)
- X if (print_kill(kl) < 0) goto out;
- X
- X out:
- X pg_end();
- X}
- END_OF_FILE
- if test 21836 -ne `wc -c <'kill.c'`; then
- echo shar: \"'kill.c'\" unpacked with wrong size!
- fi
- # end of 'kill.c'
- fi
- if test -f 'sort.c' -a "${1}" != "-c" ; then
- echo shar: Will not clobber existing file \"'sort.c'\"
- else
- echo shar: Extracting \"'sort.c'\" \(7546 characters\)
- sed "s/^X//" >'sort.c' <<'END_OF_FILE'
- X/*
- X * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
- X *
- X * Article sorting.
- X */
- X
- X#include "config.h"
- X#include "articles.h"
- X
- X
- X
- Xexport int subject_match_limit = 20; /* "strncmp" limit for subjects */
- Xexport int match_skip_prefix = 0; /* skip first N bytes in matches */
- Xexport int match_parts_equal = 0; /* match digits as equal */
- X
- Xexport int sort_mode = 1; /* default sort mode */
- X
- X/*
- X * String matching macroes.
- X *
- X * MATCH_DROP(t, a) and MATCH_DROP(t, b) must both be proven false
- X * before MATCH_???(t, a, b) is used.
- X */
- X
- X#define MATCH_DROP(table, c) ( c & 0200 || table[c] == 0 )
- X#define MATCH_EQ(table, a, b) ( a == b || table[a] == table[b] )
- X#define MATCH_LS_EQ(table, a, b) ( a <= b || table[a] <= table[b] )
- X#define MATCH_LS(table, a, b) ( a < b || table[a] < table[b] )
- X#define MATCH_CMP(table, a, b) (table[a] - table[b])
- X
- Xstatic char match_subject[128] = {
- X
- X/* NUL SOH STX ETX EOT ENQ ACK BEL BS TAB NL VT FF CR SO SI */
- X 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
- X
- X/* DLE DC1 DC2 DC3 DC4 NAK SYN ETB CAN EM SUB ESC FS GS RS US */
- X 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00,
- X
- X/* SP ! " # $ % & ' ( ) * + , - . / */
- X 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 00, 99, 00, 00, 00, 00,
- X/* ^^ */
- X
- X/* 0 1 2 3 4 5 6 7 8 9 : ; < = > ? */
- X 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 00, 00, 00, 00, 00, 00,
- X
- X/* @ A B C D E F G H I J K L M N O */
- X 00, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
- X
- X/* P Q R S T U V W X Y Z [ \ ] ^ _ */
- X 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 00, 00,
- X
- X/* ` a b c d e f g h i j k l m n o */
- X 00, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
- X
- X/* p q r s t u v w x y z { | } ~ DEL */
- X 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 00, 00
- X};
- X
- X
- Xstatic order_subj_date(ah1, ah2)
- Xarticle_header **ah1, **ah2;
- X{
- X register char *a = (**ah1).subject, *b = (**ah2).subject;
- X register char ca, cb;
- X register int p, len;
- X
- X if (match_skip_prefix) {
- X if ((**ah1).subj_length > match_skip_prefix) {
- X if ((**ah2).subj_length > match_skip_prefix) {
- X a += match_skip_prefix;
- X b += match_skip_prefix;
- X } else
- X return 1;
- X } else {
- X if ((**ah2).subj_length > match_skip_prefix) {
- X return -1;
- X }
- X }
- X }
- X
- X for (len = 0; ; len++, a++, b++) {
- X while ((ca = *a) && MATCH_DROP(match_subject, ca)) a++;
- X while ((cb = *b) && MATCH_DROP(match_subject, cb)) b++;
- X
- X if (ca == NUL) {
- X if (cb == NUL) break;
- X if (len >= subject_match_limit) break;
- X return -1;
- X }
- X
- X if (cb == NUL) {
- X if (len >= subject_match_limit) break;
- X return 1;
- X }
- X
- X if (p = MATCH_CMP(match_subject, ca, cb)) return p;
- X }
- X
- X if ((**ah1).t_stamp > (**ah2).t_stamp) return 1;
- X if ((**ah1).t_stamp < (**ah2).t_stamp) return -1;
- X return 0;
- X}
- X
- X/* data_subj_date can only be used after root_t_stamp is set */
- X
- Xstatic order_date_subj_date(ah1, ah2)
- Xarticle_header **ah1, **ah2;
- X{
- X register time_stamp t1 = (**ah1).root_t_stamp;
- X register time_stamp t2 = (**ah2).root_t_stamp;
- X
- X if (t1 > t2) return 1;
- X if (t1 < t2) return -1;
- X return order_subj_date(ah1, ah2); /* get original order */
- X}
- X
- X
- Xstatic order_arrival(a, b)
- Xarticle_header **a, **b;
- X{
- X register long i;
- X
- X if ((i = (int)((*a)->a_number - (*b)->a_number)) == 0)
- X i = (*a)->fpos - (*b)->fpos;
- X
- X return (i > 0) ? 1 : (i < 0) ? -1 : 0;
- X}
- X
- Xstatic order_date(ah1, ah2)
- Xregister article_header **ah1, **ah2;
- X{
- X if ((**ah1).t_stamp > (**ah2).t_stamp) return 1;
- X if ((**ah1).t_stamp < (**ah2).t_stamp) return -1;
- X return 0;
- X}
- X
- Xstatic order_from_date(ah1, ah2)
- Xregister article_header **ah1, **ah2;
- X{
- X register int i;
- X
- X if (i = strcmp((**ah1).sender, (**ah2).sender)) return i;
- X return order_date(ah1, ah2);
- X}
- X
- Xstatic flag_type article_equal(ah1, ah2) /* ah1.hdr == ah2.hdr */
- Xarticle_header **ah1, **ah2;
- X{
- X register char *a = (**ah1).subject, *b = (**ah2).subject;
- X register int len;
- X
- X if (match_skip_prefix) {
- X if ((**ah1).subj_length > match_skip_prefix) {
- X if ((**ah2).subj_length > match_skip_prefix) {
- X a += match_skip_prefix;
- X b += match_skip_prefix;
- X }
- X }
- X }
- X
- X for (len = 0;; len++, a++, b++) {
- X while (*a && MATCH_DROP(match_subject, *a)) a++;
- X while (*b && MATCH_DROP(match_subject, *b)) b++;
- X
- X if (*a == NUL) {
- X if (*b == NUL) break;
- X if (len >= subject_match_limit) return A_ALMOST_SAME;
- X return 0;
- X }
- X
- X if (*b == NUL) {
- X if (len >= subject_match_limit) return A_ALMOST_SAME;
- X return 0;
- X }
- X
- X if (!MATCH_EQ(match_subject, *a, *b)) {
- X if (len >= subject_match_limit)
- X return A_ALMOST_SAME;
- X if (match_parts_equal && isdigit(*a) && isdigit(*b))
- X return A_ALMOST_SAME;
- X return 0;
- X }
- X }
- X
- X return A_SAME;
- X}
- X
- Xsort_articles(mode)
- Xint mode;
- X{
- X register article_header **app;
- X register long n;
- X register flag_type same;
- X fct_type cmp;
- X
- X for (n = n_articles; --n >= 0;)
- X articles[n]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME);
- X
- X if (n_articles <= 1) return;
- X
- X if (mode == -1) mode = sort_mode;
- X
- X switch (mode) {
- X default:
- X case 0: /* arrival (no sort) */
- X cmp = order_arrival;
- X break;
- X case 1: /* date-subject-date */
- X case 2: /* subject-date */
- X cmp = order_subj_date;
- X break;
- X case 3: /* date only */
- X cmp = order_date;
- X break;
- X case 4: /* sender-date */
- X cmp = order_from_date;
- X break;
- X }
- X
- X quicksort(articles, n_articles, article_header *, cmp);
- X
- X articles[0]->root_t_stamp = articles[0]->t_stamp;
- X for (n = n_articles - 1, app = articles + 1; --n >= 0; app++) {
- X if (same = article_equal(app, app - 1)) {
- X app[0]->root_t_stamp = app[-1]->root_t_stamp;
- X app[0]->flag |= same;
- X app[-1]->flag |= A_NEXT_SAME;
- X } else {
- X app[0]->root_t_stamp = app[0]->t_stamp;
- X }
- X }
- X
- X if (mode == 1)
- X quicksort(articles, n_articles, article_header *, order_date_subj_date);
- X}
- X
- X
- X/*
- X * Eliminate articles with the A_KILL flag set preserving the present ordering.
- X * This will only release the last entries in the articles array.
- X * Neither strings nor articles headers are released.
- X */
- X
- Xelim_articles(list, list_lgt)
- Xregister article_number *list;
- Xint list_lgt;
- X{
- X register article_header **srca, **desta;
- X register article_number n, count;
- X register flag_type same;
- X int changed, llen;
- X
- X count = 0;
- X changed = 0, llen = 0;
- X for (n = 0, srca = desta = articles; n < n_articles; n++, srca++) {
- X if ((*srca)->attr == A_KILL) {
- X if (list_lgt > 0) {
- X if (n < *list) {
- X if (llen) changed = 1;
- X } else
- X if (n == *list) {
- X if (llen) {
- X llen++;
- X list_lgt--;
- X *list++ = -1;
- X } else
- X ++(*list);
- X changed = 1;
- X }
- X }
- X continue;
- X }
- X if (list_lgt > 0 && n == *list) {
- X *list++ = count;
- X list_lgt--;
- X llen++;
- X }
- X count++;
- X *desta++ = *srca;
- X }
- X if (list_lgt > 0) {
- X if (!llen) *list = 0;
- X changed = 1;
- X }
- X n_articles = count;
- X
- X if (changed && n_articles > 0) {
- X srca = articles;
- X srca[0]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME);
- X for (n = n_articles - 1, srca++; --n >= 0; srca++) {
- X srca[0]->flag &= ~(A_SAME|A_ALMOST_SAME|A_NEXT_SAME);
- X if (same = article_equal(srca, srca - 1)) {
- X srca[0]->flag |= same;
- X srca[-1]->flag |= A_NEXT_SAME;
- X }
- X }
- X }
- X
- X return changed;
- X}
- END_OF_FILE
- if test 7546 -ne `wc -c <'sort.c'`; then
- echo shar: \"'sort.c'\" unpacked with wrong size!
- fi
- # end of 'sort.c'
- fi
- echo shar: End of archive 11 \(of 22\).
- cp /dev/null ark11isdone
- MISSING=""
- for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 ; do
- if test ! -f ark${I}isdone ; then
- MISSING="${MISSING} ${I}"
- fi
- done
- if test "${MISSING}" = "" ; then
- echo You have unpacked all 22 archives.
- rm -f ark[1-9]isdone ark[1-9][0-9]isdone
- else
- echo You still must unpack the following archives:
- echo " " ${MISSING}
- fi
- exit 0
-
- exit 0 # Just in case...
-